package com.ns.scs.task.service.impl;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ns.core.utils.sg.ResultError;
import com.ns.core.utils.thread.ThreadUtils;
import com.ns.scs.task.dto.ScheduledPageDto;
import com.ns.scs.task.entity.ScheduledTask;
import com.ns.scs.task.excutor.basic.ScheduledBasicActuator;
import com.ns.scs.task.mapper.ScheduledActuatorMapper;
import com.ns.scs.task.mapper.ScheduledTaskMapper;
import com.ns.scs.task.service.IScheduledTaskService;
import com.ns.scs.task.vo.ScheduledActuatorVO;
import com.ns.scs.task.vo.ScheduledTaskVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.lang.reflect.Constructor;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 定时任务逻辑实现
 *
 * @author liao.wei
 * @since 2024/11/11
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ScheduledTaskServiceImpl extends ServiceImpl<ScheduledTaskMapper, ScheduledTask> implements IScheduledTaskService {

    private final ScheduledTaskMapper mapper;

    private final ScheduledActuatorMapper actuatorMapper;

    /**
     * 定时任务线程池
     */

    private final ThreadPoolTaskScheduler threadPoolTaskScheduler;

    /**
     * 启动状态的定时任务集合
     */
    public Map<Long, ScheduledFuture> scheduledFutureMap = new ConcurrentHashMap<>();

    /**
     * 可重入锁
     */
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public Page<ScheduledTaskVO> getList(ScheduledPageDto dto) {
        return mapper.getList(dto, new Page<>(dto.getCurrent(), dto.getSize()));
    }

    @Override
    public void start(Long id) {
        // 根据id查询任务
        ScheduledTask scheduledTask = mapper.selectById(id);
        if (Objects.nonNull(scheduledTask)) {
            ScheduledTaskVO vo = new ScheduledTaskVO();
            BeanUtils.copyProperties(scheduledTask, vo);
            // 判断任务是否启用
            if (1 != scheduledTask.getStatus()) {
                throw new ResultError("定时任务未启用，无法执行！");
            } else {
                try {
                    if (Boolean.TRUE.equals(this.isStart(id))) {
                        throw new ResultError("当前任务在启动状态中");
                    } else {
                        // 添加锁放一个线程启动，防止多人启动多次
                        lock.lock();
                        // 任务启动
                        this.doStartTask(vo);
                    }
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    @Override
    public void stop(Long id) {
        // 根据id查询任务
        log.info("停止任务： " + id);
        if (scheduledFutureMap.containsKey(id)) {
            ScheduledFuture scheduledFuture = scheduledFutureMap.get(id);
            scheduledFuture.cancel(true);
            scheduledFutureMap.remove(id);
        }
    }

    /**
     * 重启定时任务
     *
     * @param id 任务id
     */
    @Override
    public void restart(Long id) {
        // 停止
        this.stop(id);
        // 启动
        this.start(id);
    }

    @Override
    public void initTask() {
        List<ScheduledTaskVO> list = mapper.selectScheduled();
        if (CollectionUtils.isNotEmpty(list)) {
            // 循环所有定时任务
            for (ScheduledTaskVO st : list) {
                // 执行任务
                doStartTask(st);
            }
        }
    }

    /**
     * 执行启动任务
     *
     * @param scheduled 定时任务
     */
    private void doStartTask(ScheduledTaskVO scheduled) {
        // 查询执行器
        ScheduledActuatorVO actuatorVO = actuatorMapper.getById(scheduled.getActuatorId());
        if (Objects.isNull(actuatorVO)) {
            return;
        }
        try {
            // 通过类全路径找到类
            Class<?> clazz = Class.forName(actuatorVO.getActuatorClass());
            // 获取构造函数
            Constructor<?> cons = clazz.getConstructor(ScheduledTaskVO.class);
            // 实例化对象，传入参数，用于执行器关联业务
            ScheduledBasicActuator task = (ScheduledBasicActuator) cons.newInstance(scheduled);
            // 检查执行器是否继承父类，如果未继承，则直接中断
            Assert.isAssignable(ScheduledBasicActuator.class, task.getClass(), "定时任务业务类必须继承ScheduledOfTask父类");
            // 实例化任务触发器
            CronTrigger cronTrigger = new CronTrigger(scheduled.getCronExpression());
            // 实例化定时任务
            ScheduledFuture<?> scheduledFuture = threadPoolTaskScheduler.schedule(task, cronTrigger);
            // 存入定时任务，用于查找和停用
            scheduledFutureMap.put(scheduled.getId(), scheduledFuture);
            // 执行任务
            ThreadPoolExecutor threadPoolExecutor = ThreadUtils.getThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1));
            threadPoolExecutor.execute(task);
        } catch (Exception e) {
            throw new ResultError("定时任务【" + scheduled.getTaskName() + "】启动异常。");
        }
    }

    /**
     * 检查任务是否已经启动
     *
     * @param id 定时任务id
     * @return Boolean
     */
    private Boolean isStart(Long id) {
        // 校验是否已经启动
        if (scheduledFutureMap.containsKey(id)) {
            return !scheduledFutureMap.get(id).isCancelled();
        }
        return false;
    }
}